//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation

Module Name:

gprs.cpp

Abstract:


Notes:


--*/
/*
------------------------------------------------------------------------------
    Revision History
    Date                Author            Activity ID            Activity Headline
    2007-02-08          qinxuetao         CEDB200043651          Update ril driver for network and data service
    2007-04-26          qinxuetao         WM600002433            Add +TEDGE to get the access technology
------------------------------------------------------------------------------
*/
#include "precomp.h"


// According to GSM 03.60 Section 14.9, APN's meet the DNS naming convention.
#define VALID_GPRS_APN_CHARACTERS L"ABDCEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890.-"


// Note: Remember all LISTSTRINGMAP/LISTVALUEMAP structures must be sorted by the string/value key (not the flags key).
// This is because the range parse functions use bsearch to search for the flags fields.

static const LISTSTRINGMAP g_GprsPDPTypes[] =
{
    {"IP",    RIL_GPRSPROTOCOL_IP    },
    {"OSPIH", RIL_GPRSPROTOCOL_IHOSP },
    {"PPP",   RIL_GPRSPROTOCOL_PPP   },
    {"X25",   RIL_GPRSPROTOCOL_X25   },
};
#define NUM_GPRSPDPTYPES     (sizeof(g_GprsPDPTypes) / sizeof(LISTSTRINGMAP))

static const LISTSTRINGMAP g_GprsL2Protocols[] =
{
    {"NULL",  RIL_GPRSL2PROTOCOL_NULL },
    {"PAD",   RIL_GPRSL2PROTOCOL_PAD  },
    {"PPP",   RIL_GPRSL2PROTOCOL_PPP  },
    {"X25",   RIL_GPRSL2PROTOCOL_X25  },
};
#define NUM_GPRSL2PROTOCOLS (sizeof(g_GprsL2Protocols) / sizeof(LISTSTRINGMAP))

static const LISTVALUEMAP g_GprsDataComp[] =
{
    {0, RIL_GPRSDATACOMP_OFF},
    {1, RIL_GPRSDATACOMP_ON}
};
#define NUM_GPRSDATACOMP (sizeof(g_GprsDataComp) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsHeaderComp[] =
{
    {0, RIL_GPRSHEADERCOMP_OFF},
    {1, RIL_GPRSHEADERCOMP_ON}
};
#define NUM_GPRSHEADERCOMP (sizeof(g_GprsHeaderComp) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsPrecedenceClass[] =
{
    {0, RIL_GPRSPRECEDENCECLASS_SUBSCRIBED },
    {1, RIL_GPRSPRECEDENCECLASS_HIGH       },
    {2, RIL_GPRSPRECEDENCECLASS_NORMAL     },
    {3, RIL_GPRSPRECEDENCECLASS_LOW        }
};
#define NUM_GPRSPRECEDENCECLASS (sizeof(g_GprsPrecedenceClass) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsDelayClass[] =
{
    {0, RIL_GPRSDELAYCLASS_SUBSCRIBED  },
    {1, RIL_GPRSDELAYCLASS_PREDICTIVE1 },
    {2, RIL_GPRSDELAYCLASS_PREDICTIVE2 },
    {3, RIL_GPRSDELAYCLASS_PREDICTIVE3 },
    {4, RIL_GPRSDELAYCLASS_BESTEFFORT  }
};
#define NUM_GPRSDELAYCLASS (sizeof(g_GprsDelayClass) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsReliabilityClass[] =
{
    {0, RIL_GPRSRELIABILITYCLASS_SUBSCRIBED },
    {1, RIL_GPRSRELIABILITYCLASS_1          },
    {2, RIL_GPRSRELIABILITYCLASS_2          },
    {3, RIL_GPRSRELIABILITYCLASS_3          },
    {4, RIL_GPRSRELIABILITYCLASS_4          },
    {5, RIL_GPRSRELIABILITYCLASS_5          }
};
#define NUM_GPRSRELIABILITYCLASS (sizeof(g_GprsReliabilityClass) / sizeof(LISTVALUEMAP))

static const LISTSTRINGMAP g_GprsClass[] =
{
    {"A",    RIL_GPRSCLASS_GSMANDGPRS            },
    {"B",    RIL_GPRSCLASS_GSMORGPRS             },
    {"C",    RIL_GPRSCLASS_GSMORGPRS_EXCLUSIVE   },
    {"CC",   RIL_GPRSCLASS_GSMONLY               },
    {"CG",   RIL_GPRSCLASS_GPRSONLY              },
};
#define NUM_GPRSCLASS (sizeof(g_GprsClass) / sizeof(LISTSTRINGMAP))

static const LISTVALUEMAP g_GprsPeakThruClass[] =
{
    {0, RIL_PEAKTHRUCLASS_SUBSCRIBED },
    {1, RIL_PEAKTHRUCLASS_8000       },
    {2, RIL_PEAKTHRUCLASS_16000      },
    {3, RIL_PEAKTHRUCLASS_32000      },
    {4, RIL_PEAKTHRUCLASS_64000      },
    {5, RIL_PEAKTHRUCLASS_128000     },
    {6, RIL_PEAKTHRUCLASS_256000     },
    {7, RIL_PEAKTHRUCLASS_512000     },
    {8, RIL_PEAKTHRUCLASS_1024000    },
    {9, RIL_PEAKTHRUCLASS_2048000    }
};
#define NUM_GPRSPEAKTHRUCLASS (sizeof(g_GprsPeakThruClass) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsMeanThruClass[] =
{
    {0,  RIL_MEANTHRUCLASS_SUBSCRIBED },
    {1,  RIL_MEANTHRUCLASS_100        },
    {2,  RIL_MEANTHRUCLASS_200        },
    {3,  RIL_MEANTHRUCLASS_500        },
    {4,  RIL_MEANTHRUCLASS_1000       },
    {5,  RIL_MEANTHRUCLASS_2000       },
    {6,  RIL_MEANTHRUCLASS_5000       },
    {7,  RIL_MEANTHRUCLASS_10000      },
    {8,  RIL_MEANTHRUCLASS_20000      },
    {9,  RIL_MEANTHRUCLASS_50000      },
    {10, RIL_MEANTHRUCLASS_100000     },
    {11, RIL_MEANTHRUCLASS_200000     },
    {12, RIL_MEANTHRUCLASS_500000     },
    {13, RIL_MEANTHRUCLASS_1000000    },
    {14, RIL_MEANTHRUCLASS_2000000    },
    {15, RIL_MEANTHRUCLASS_5000000    },
    {16, RIL_MEANTHRUCLASS_10000000   },
    {17, RIL_MEANTHRUCLASS_20000000   },
    {18, RIL_MEANTHRUCLASS_50000000   },
    {31, RIL_MEANTHRUCLASS_DONTCARE   }
};
#define NUM_GPRSMEANTHRUCLASS (sizeof(g_GprsMeanThruClass) / sizeof(LISTVALUEMAP))

static const LISTVALUEMAP g_GprsMOSMSService[] =
{
    //  3GPP TS 27.007 V6.8.0 (2005-03) section 10.1.20 +CGSMS
    {0,  RIL_MOSMSSERVICE_GPRS              },
    {1,  RIL_MOSMSSERVICE_CIRCUIT           },
    {2,  RIL_MOSMSSERVICE_GPRSPREFERRED     },
    {3,  RIL_MOSMSSERVICE_CIRCUITPREFERRED  }
};
#define NUM_GPRSMOSMSSERVICE (sizeof(g_GprsMOSMSService) / sizeof(LISTVALUEMAP))

/*
+CGDCONT: <cid>, <PDP_type>,<APN>,<PDP_addr>,<data_comp>,<head_comp>[,<pd1>[,...[,pdN]]]
[<CR><LF>+CGDCONT: <cid>, <PDP_type>,<APN>,<PDP_addr>, <data_comp>,<head_comp>[,<pd1>[,...[,pdN]]]
[...]]

Returns a RILGPRSCONTEXT structure
*/

HRESULT ParseGetGPRSContextList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGPRSContextList);

    PBYTE pBuffer=NULL;
    DWORD dwBufferSize=0;

    RILGPRSCONTEXT GPRSContext, *pGPRSContext;

    char szValue[MAX_PATH];
    UINT nValue;
    LPCSTR szParams, szParamsEnd;

    HRESULT hr = E_FAIL;
    BOOL bSuccess;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+CGDCONT: "
    while (MatchStringBeginning(szRsp, "+CGDCONT: ", szRsp))
    {
        memset(&GPRSContext, 0x00, sizeof(GPRSContext));

        // Parse <cid>
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        GPRSContext.dwContextID = nValue;
        GPRSContext.dwParams|= RIL_PARAM_GCONT_CONTEXTID;

        // Parse ,<PDP_type>
        if (!MatchStringBeginning(szRsp, ",", szRsp))
        {
            goto Continue;
        }

        if (ParseString(szRsp, szValue, MAX_PATH, szRsp)
            && FlagFromString(szValue, g_GprsPDPTypes, NUM_GPRSPDPTYPES, GPRSContext.dwProtocolType))
        {
            GPRSContext.dwParams|= RIL_PARAM_GCONT_PROTOCOLTYPE;
        }

        // Parse ,<APN>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseString(szRsp, szValue, MAX_PATH, szRsp))
        {
            goto Continue;
        }
        (void)wcsncpyz(GPRSContext.wszAccessPointName, WideString(szValue), MAXLENGTH_GPRSACCESSPOINTNAME);
        GPRSContext.dwParams|= RIL_PARAM_GCONT_ACCESSPOINTNAME;

#ifdef RIL_WATSON_REPORT
        // Copy APN to the info cache.
        EnterCriticalSection(&g_csRilInfoCache);
        strncpy(g_RilInfoCache.szAPN, szValue, MAXLENGTH_GPRSACCESSPOINTNAME);
        LeaveCriticalSection(&g_csRilInfoCache);
#endif // RIL_WATSON_REPORT

        // Parse ,<PDP_addr>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseString(szRsp, szValue, MAX_PATH, szRsp))
        {
            goto Continue;
        }
        (void)wcsncpyz(GPRSContext.wszAddress, WideString(szValue), MAXLENGTH_GPRSADDRESS);
        GPRSContext.dwParams|= RIL_PARAM_GCONT_ADDRESS;

        // Parse ,<data_comp>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x2, nValue, szRsp))
        {
            goto Continue;
        }

        if (FlagFromValue(nValue, g_GprsDataComp, NUM_GPRSDATACOMP, GPRSContext.dwDataCompression))
        {
            GPRSContext.dwParams|= RIL_PARAM_GCONT_DATACOMPRESSION;
        }

        // Parse ,<head_comp>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x2, nValue, szRsp))
        {
            goto Continue;
        }

        if (FlagFromValue(nValue, g_GprsHeaderComp, NUM_GPRSHEADERCOMP, GPRSContext.dwHeaderCompression))
        {
            GPRSContext.dwParams|= RIL_PARAM_GCONT_HEADERCOMPRESSION;
        }

        // Parse ","
        if (!MatchStringBeginning(szRsp, ",", szRsp))
        {
            goto Continue;
        }

        szParams = szRsp;
        // Parse ,<pd1>[,...[,pdN]]
        szParamsEnd = strstr(szParams, "\r\n");
        if (!szParamsEnd)
        {
            goto Error;
        }

        szRsp = szParamsEnd;

        // Now all the data between szParams and szParamsEnd is the pd1-N string
        GPRSContext.dwParams|= RIL_PARAM_GCONT_PARAMETERS;

        // Set dwParameterLength to be size of string in bytes counting null char at end
        GPRSContext.dwParameterLength = (szParamsEnd-szParams)+1;

        Continue:
        // Find "<postfix>"
        bSuccess = FindRspPostfix(szRsp, szRsp);
        if (!bSuccess)
        {
            goto Error;
        }

        // Init size of GPRSContext
        GPRSContext.cbSize = sizeof(GPRSContext);

        // Add in size of param string
        GPRSContext.cbSize += GPRSContext.dwParameterLength;

        // Round up to the nearest 32-bit address for alignment
        GPRSContext.cbSize = (GPRSContext.cbSize + 3) & (~0x03);

        // Reallocate our buffer for the additional data
        pBuffer = (PBYTE)ReallocBlob(pBuffer,dwBufferSize+GPRSContext.cbSize);
        if (!pBuffer)
        {
            goto Error;
        }

        // Point pGPRSContext to the first byte of the newly allocated section
        pGPRSContext = (RILGPRSCONTEXT *) (pBuffer+dwBufferSize);

        // Copy struct to buffer
        *pGPRSContext = GPRSContext;

        if (GPRSContext.dwParameterLength)
        {
            memcpy(GPRSContext.szParameters,szParams,GPRSContext.dwParameterLength);
            GPRSContext.szParameters[GPRSContext.dwParameterLength]='\0';
        }

        dwBufferSize+=GPRSContext.cbSize;
    }

    pBlob = (void*)pBuffer;
    cbBlob = dwBufferSize;
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pBuffer);
    }
    return hr;
}

// +CGDCONT?
HRESULT RILDrv_GetGPRSContextList (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetGPRSContextList);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_GetGPRSContextList\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!QueueCmd(pHandle,
                  "AT+CGDCONT?\r",
                  CMDOPT_NONE,
                  APIID_GETGPRSCONTEXTLIST,
                  ParseGetGPRSContextList,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGDCONT=<params>
// +CGDCONT=[<cid> [,<PDP_type> [,<APN> [,<PDP_addr> [,<d_comp> [,<h_comp> [,<pd1> [,...[,pdN]]]]]]]]]
/*
typedef struct rilgprscontext_tag {
    DWORD cbSize;                       // @field structure size in bytes
    DWORD dwParams;                     // @field indicates valid parameters
    DWORD dwContextID;                  // @field the context number
    DWORD dwProtocolType;               // @field a RIL_GPRSPROTOCOL_*constant
    WCHAR wszAccessPointName[MAXLENGTH_GPRSACCESSPOINTNAME];
                                        // @field a logical name to select the gateway gprs
                                        //        (which defines the external packet data network to use)
    WCHAR wszAddress[MAXLENGTH_GPRSADDRESS]; // @field the packet address to use (if null, request dynamic)
    DWORD dwDataCompression;             // @field a RIL_GPRSDATACOMP_*
    DWORD dwHeaderCompression;           // @field a RIL_GPRSHEADERCOMP_*
    DWORD dwParameterLength;            // @field length of parameters list
    char szParameters[];              // @field parameters specific to the prococol type
} RILGPRSCONTEXT, *LPRILGPRSCONTEXT;
*/
HRESULT RILDrv_SetGPRSContext (DWORD dwParam, const RILGPRSCONTEXT* lpGprsContext)
{
    FUNCTION_TRACE(RILDrv_SetGPRSContext);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_SetGPRSContext\r\n")));

    HRESULT hr = E_INVALIDARG;
    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    UINT Value;
    RILGPRSCONTEXT RilGprsContextCopy;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpGprsContext)
    {
        goto Error;
    }

    CeSafeCopyMemory((LPVOID)&RilGprsContextCopy, (LPCVOID)lpGprsContext, sizeof(RilGprsContextCopy));

    // Build command
    (void)_snprintfz(szCmd, MAX_PATH, "AT+CGDCONT=%u,", RilGprsContextCopy.dwContextID);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (RilGprsContextCopy.dwParams & RIL_PARAM_GCONT_PROTOCOLTYPE)
    {
        LPSTR szString;
        if (!StringFromFlag(RilGprsContextCopy.dwProtocolType,g_GprsPDPTypes, NUM_GPRSPDPTYPES, szString))
        {
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "\"%s\"",szString);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (RilGprsContextCopy.dwParams & RIL_PARAM_GCONT_ACCESSPOINTNAME)
    {
        // Force the NULL-termination
        RilGprsContextCopy.wszAccessPointName[ARRAY_LENGTH(RilGprsContextCopy.wszAccessPointName)-1]=WCHAR ('\0');
        // Filter the access point name to only contain valid characters.
        WCHAR wszFilteredApn[MAXLENGTH_GPRSACCESSPOINTNAME];
        if (FAILED( StringFilterW(wszFilteredApn,
                                  ARRAY_LENGTH(wszFilteredApn)-1,
                                  (LPWSTR)RilGprsContextCopy.wszAccessPointName,
                                  VALID_GPRS_APN_CHARACTERS) ))
        {
            goto Error;
        }
        // Force the NULL-termination
        wszFilteredApn[ARRAY_LENGTH(wszFilteredApn)-1] = WCHAR ('\0');
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "\"%s\"", AnsiString(wszFilteredApn));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

#ifdef RIL_WATSON_REPORT
        // Copy APN being set to the info cache
        EnterCriticalSection(&g_csRilInfoCache);
        strncpy(g_RilInfoCache.szAPN, AnsiString(wszFilteredApn), MAXLENGTH_GPRSACCESSPOINTNAME);
        LeaveCriticalSection(&g_csRilInfoCache);
#endif // RIL_WATSON_REPORT

    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (RilGprsContextCopy.dwParams & RIL_PARAM_GCONT_ADDRESS)
    {
        // Force the NULL-termination
        RilGprsContextCopy.wszAddress[ARRAY_LENGTH(RilGprsContextCopy.wszAddress)-1]=WCHAR ('\0');

        // Filter the address to only contain valid characters. Valid characters are defined the
        // same as the APN, but could possibly be limited even more to digits and periods.
        WCHAR wszFilteredAddr[MAXLENGTH_GPRSADDRESS];
        if (FAILED( StringFilterW(wszFilteredAddr,
                                  ARRAY_LENGTH(wszFilteredAddr),
                                  (LPWSTR)RilGprsContextCopy.wszAddress,
                                  VALID_GPRS_APN_CHARACTERS) ))
        {
            goto Error;
        }
        // Force the NULL-termination
        wszFilteredAddr[ARRAY_LENGTH(wszFilteredAddr)-1] = WCHAR ('\0');
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "\"%s\"", AnsiString(wszFilteredAddr));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (RilGprsContextCopy.dwParams & RIL_PARAM_GCONT_DATACOMPRESSION)
    {
        if (!ValueFromFlag(RilGprsContextCopy.dwDataCompression,g_GprsDataComp,NUM_GPRSDATACOMP,Value))
        {
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u",Value);

        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add ","
    (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (RilGprsContextCopy.dwParams & RIL_PARAM_GCONT_HEADERCOMPRESSION)
    {
        if (!ValueFromFlag(RilGprsContextCopy.dwHeaderCompression,g_GprsHeaderComp,NUM_GPRSHEADERCOMP,Value))
        {
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u",Value);

        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

#ifndef EMP_DRIVER
    // EMP does not support <pd1>...<pdN>
    if (RilGprsContextCopy.dwParams & RIL_PARAM_GCONT_PARAMETERS)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), ",%s", RilGprsContextCopy.szParameters);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }
#endif

    // Add "\r"
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));

    if (!QueueCmd(pHandle,
                  szCmd,
                  CMDOPT_NONE,
                  APIID_SETGPRSCONTEXT,
                  NULL,     // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

HRESULT RILDrv_NdisSetGPRSContextActivated (DWORD dwParam, const RILNDISGPRSCONTEXT *lpNdisSetGprsContextActivated )
{
    HRESULT hr = E_INVALIDARG;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if ( lpNdisSetGprsContextActivated && (lpNdisSetGprsContextActivated->cbSize == sizeof(*lpNdisSetGprsContextActivated)))
    {
       hr = pHandle->CRilInstanceNdis.NdisSetGPRSContextActivated( lpNdisSetGprsContextActivated);
    }

    Error:
    return hr;
}

HRESULT RILDrv_NdisReceivePacketDone (DWORD dwParam,  RILNDISPACKET* lpNdisPacket)
{
    HRESULT hr = E_INVALIDARG;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

   hr = pHandle->CRilInstanceNdis.NdisReceivePacketDone(lpNdisPacket);

    Error:
    return hr;
}

HRESULT RILDrv_NdisSendPacket (DWORD dwParam,  RILNDISPACKET* lpNdisPacket)
{
    HRESULT hr = E_INVALIDARG;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        hr = E_FAIL;
        goto Error;
    }

   hr = pHandle->CRilInstanceNdis.NdisSendPacket(lpNdisPacket);

    Error:
    return hr;
}
// +CGDCONT=<dwContextID>
HRESULT RILDrv_DeleteGPRSContext (DWORD dwParam, DWORD dwContextID)
{
    FUNCTION_TRACE(RILDrv_DeleteGPRSContext);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_DeleteGPRSContext\r\n")));

#ifdef GPRS_CONTEXT_CACHING
    ClearGPRSContextCommandCache();
#endif // GPRS_CONTEXT_CACHING

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_PATH];
#ifdef EMP_DRIVER
    // EMP use *EIAD to delete a context
    (void)_snprintfz(szCmd, MAX_PATH, "AT*EIAD=%u,1\r", dwContextID);
#else
    (void)_snprintfz(szCmd, MAX_PATH, "AT+CGDCONT=%u\r", dwContextID);
#endif
    if (!QueueCmd(pHandle,
                  szCmd,
                  CMDOPT_NONE,
                  APIID_DELETEGPRSCONTEXT,
                  NULL,     // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

#ifdef RIL_WATSON_REPORT
    // Reset Ril Info cache of the APN
    *(g_RilInfoCache.szAPN) = '\0';
#endif // RIL_WATSON_REPORT

    Error:
    return hr;
}

/*
+XXXXXX: <cid>,<precedence>,<delay>,<reliability>,<peak>,<mean>
[<CR><LF>+XXXXXX: <cid>,<precedence>,<delay>,<reliability.>,<peak>,<mean>
[...]]
*/
HRESULT ParseQOSList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPCSTR szCmd)
{
    FUNCTION_TRACE(ParseGetRequestedQualityOfServiceList);
    PBYTE pBuffer=NULL;
    DWORD dwBufferSize=0;

    RILGPRSQOSPROFILE *pQOSProfile;

    UINT nValue;

    HRESULT hr = E_FAIL;
    BOOL bSuccess;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+XXXXXX: "
    while (MatchStringBeginning(szRsp, szCmd, szRsp))
    {
        // Reallocate our buffer for the additional data
        pBuffer = (PBYTE)ReallocBlob(pBuffer,dwBufferSize+sizeof(RILGPRSQOSPROFILE));
        if (!pBuffer)
        {
            goto Error;
        }

        pQOSProfile = (RILGPRSQOSPROFILE *)(pBuffer+dwBufferSize);
        memset(pQOSProfile, 0x00, sizeof(RILGPRSQOSPROFILE));
        pQOSProfile->cbSize=sizeof(RILGPRSQOSPROFILE);

        dwBufferSize+=sizeof(RILGPRSQOSPROFILE);

        // Parse <cid>
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        pQOSProfile->dwContextID=nValue;
        pQOSProfile->dwParams|= RIL_PARAM_GQOSP_CONTEXTID;

        // Parse ,<precedence>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            (!ParseUInt(szRsp, TRUE, nValue, szRsp)) )
        {
            goto Continue;
        }

        if (FlagFromValue(nValue,g_GprsPrecedenceClass,NUM_GPRSPRECEDENCECLASS,pQOSProfile->dwPrecedenceClass))
        {
            pQOSProfile->dwParams|= RIL_PARAM_GQOSP_PRECEDENCECLASS;
        }

        // Parse ,<delay>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            (!ParseUInt(szRsp, TRUE, nValue, szRsp)) )
        {
            goto Continue;
        }

        if (FlagFromValue(nValue,g_GprsDelayClass,NUM_GPRSDELAYCLASS, pQOSProfile->dwDelayClass))
        {
            pQOSProfile->dwParams|= RIL_PARAM_GQOSP_DELAYCLASS;
        }

        // Parse ,<reliability>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            (!ParseUInt(szRsp, TRUE, nValue, szRsp)) )
        {
            goto Continue;
        }

        if (FlagFromValue(nValue,g_GprsReliabilityClass,NUM_GPRSRELIABILITYCLASS,pQOSProfile->dwReliabilityClass))
        {
            pQOSProfile->dwParams|= RIL_PARAM_GQOSP_RELIABILITYCLASS;
        }

        // Parse ,<peak>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            (!ParseUInt(szRsp, TRUE, nValue, szRsp)) )
        {
            goto Continue;
        }

        if (FlagFromValue(nValue,g_GprsPeakThruClass,NUM_GPRSPEAKTHRUCLASS,pQOSProfile->dwPeakThruClass))
        {
            pQOSProfile->dwParams|= RIL_PARAM_GQOSP_PEAKTHRUCLASS;
        }

        // Parse ,<mean>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            (!ParseUInt(szRsp, TRUE, nValue, szRsp)) )
        {
            goto Continue;
        }

        if (FlagFromValue(nValue,g_GprsMeanThruClass,NUM_GPRSMEANTHRUCLASS,pQOSProfile->dwMeanThruClass))
        {
            pQOSProfile->dwParams|= RIL_PARAM_GQOSP_MEANTHRUCLASS;
        }

        Continue:
        // Find "<postfix>"
        bSuccess = FindRspPostfix(szRsp, szRsp);
        if (!bSuccess)
        {
            goto Error;
        }
    }

    pBlob = (void*)pBuffer;
    cbBlob = dwBufferSize;
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pBuffer);
    }
    return hr;
}

/*
+CGQREQ: <cid>, <precedence >, <delay>,<reliability>, <peak>, <mean>
[<CR><LF>+CGQREQ: <cid>, <precedence >,<delay>, <reliability.>, <peak>, <mean>
[...]]
*/
HRESULT ParseGetRequestedQualityOfServiceList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetRequestedQualityOfServiceList);
    return ParseQOSList(szRsp,pBlob,cbBlob,"+CGQREQ: ");
}

/*
+CGQMIN: <cid>, <precedence >, <delay>,<reliability>, <peak>, <mean>
[<CR><LF>+CGQMIN: <cid>, <precedence >, <delay>, <reliability.>, <peak>, <mean>
[...]]
*/
HRESULT ParseGetMinimumQualityOfServiceList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetMinimumQualityOfServiceList);
    return ParseQOSList(szRsp,pBlob,cbBlob,"+CGQMIN: ");
}

// +CGQREQ?
HRESULT RILDrv_GetRequestedQualityOfServiceList (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetRequestedQualityOfServiceList);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_GetRequestedQualityOfServiceList\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!QueueCmd(pHandle,
                  "AT+CGQREQ?\r",
                  CMDOPT_NONE,
                  APIID_GETREQUESTEDQUALITYOFSERVICELIST,
                  ParseGetRequestedQualityOfServiceList,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGQMIN?
HRESULT RILDrv_GetMinimumQualityOfServiceList (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetMinimumQualityOfServiceList);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_GetMinimumQualityOfServiceList\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!QueueCmd(pHandle,
                  "AT+CGQMIN?\r",
                  CMDOPT_NONE,
                  APIID_GETMINIMUMQUALITYOFSERVICELIST,
                  ParseGetMinimumQualityOfServiceList,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGQREQ=[<cid> [,<precedence > [,<delay> [,<reliability.> [,<peak> [,<mean>]]]]]]
// +CGQMIN=[<cid> [,<precedence > [,<delay> [,<reliability.> [,<peak> [,<mean>]]]]]]
/*
typedef struct rilgprsqosprofile_tag {
    DWORD cbSize;                       // @field structure size in bytes
    DWORD dwParams;                     // @field indicates valid parameters
    DWORD dwContextID;                  // @field the context number
    DWORD dwPrecedenceClass;            // @field a RIL_GPRSPRECEDENCECLASS_* constant
    DWORD dwDelayClass;                 // @field a RIL_GPRSDELAYCLASS_* constant
    WCHAR dwReliabilityClass;           // @field a RIL_GPRSRELIABILITYCLASS_* constant
    DWORD dwPeakThruClass;              // @field a RIL_GPRSPEAKTHRUCLASS_* constant
    DWORD dwMeanThruClass;              // @field a RIL_GPRSMEANTHRUCLASS_* constant
} RILGPRSQOSPROFILE, *LPRILGPRSQOSPROFILE;
*/
HRESULT SetQOS(DWORD dwParam, const RILGPRSQOSPROFILE* lpGprsQosProfile, LPCSTR szATCmd, const APIID apiid)
{
    FUNCTION_TRACE(RILDrv_SetRequestedQualityOfService);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_SetRequestedQualityOfService\r\n")));

    HRESULT hr = E_INVALIDARG;
    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    UINT Value;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpGprsQosProfile)
    {
        goto Error;
    }

    // Build command
#if !defined(OEM2_DRIVER)
    (void)_snprintfz(szCmd, MAX_PATH, "AT%s=%u,", szATCmd, lpGprsQosProfile->dwContextID);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    {
#else
    (void)_snprintfz(szCmd, MAX_PATH, "AT%s=%u", szATCmd, lpGprsQosProfile->dwContextID);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (lpGprsQosProfile->dwParams & (RIL_PARAM_GQOSP_PRECEDENCECLASS     |
                                      RIL_PARAM_GQOSP_DELAYCLASS          |
                                      RIL_PARAM_GQOSP_RELIABILITYCLASS    |
                                      RIL_PARAM_GQOSP_PEAKTHRUCLASS       |
                                      RIL_PARAM_GQOSP_MEANTHRUCLASS))
    {
        (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
#endif // defined(OEM2_DRIVER)
        if (lpGprsQosProfile->dwParams & RIL_PARAM_GQOSP_PRECEDENCECLASS)
        {
            if (!ValueFromFlag(lpGprsQosProfile->dwPrecedenceClass,g_GprsPrecedenceClass,NUM_GPRSPRECEDENCECLASS,Value))
            {
                goto Error;
            }
            (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u",Value);

            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }

        // Add ","
        (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

        if (lpGprsQosProfile->dwParams & RIL_PARAM_GQOSP_DELAYCLASS)
        {
            if (!ValueFromFlag(lpGprsQosProfile->dwDelayClass,g_GprsDelayClass,NUM_GPRSDELAYCLASS,Value))
            {
                goto Error;
            }
            (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u",Value);

            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }

        // Add ","
        (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);


        if (lpGprsQosProfile->dwParams & RIL_PARAM_GQOSP_RELIABILITYCLASS)
        {
            if (!ValueFromFlag(lpGprsQosProfile->dwReliabilityClass,g_GprsReliabilityClass,NUM_GPRSRELIABILITYCLASS,Value))
            {
                goto Error;
            }
            (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u",Value);

            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }

        // Add ","
        (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

        if (lpGprsQosProfile->dwParams & RIL_PARAM_GQOSP_PEAKTHRUCLASS)
        {
            if (!ValueFromFlag(lpGprsQosProfile->dwPeakThruClass,g_GprsPeakThruClass,NUM_GPRSPEAKTHRUCLASS,Value))
            {
                goto Error;
            }
            (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u",Value);

            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }

        // Add ","
        (void)strncpyz(szWalk, ",", MAX_PATH - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

        if (lpGprsQosProfile->dwParams & RIL_PARAM_GQOSP_MEANTHRUCLASS)
        {
            if (!ValueFromFlag(lpGprsQosProfile->dwMeanThruClass,g_GprsMeanThruClass,NUM_GPRSMEANTHRUCLASS,Value))
            {
                goto Error;
            }
            (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "%u",Value);

            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }
    }

    // Add "\r"
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));

    if (!QueueCmd(pHandle,
                  szCmd,
                  CMDOPT_NONE,
                  apiid,
                  NULL,     // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGQREQ=<params>
HRESULT RILDrv_SetRequestedQualityOfService (DWORD dwParam, const RILGPRSQOSPROFILE* lpGprsQosProfile)
{
    FUNCTION_TRACE(RILDrv_SetRequestedQualityOfService);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_SetRequestedQualityOfService\r\n")));
    return SetQOS(dwParam,lpGprsQosProfile, "+CGQREQ",APIID_SETREQUESTEDQUALITYOFSERVICE);
}

// +CGQMIN=<params>
HRESULT RILDrv_SetMinimumQualityOfService (DWORD dwParam, const RILGPRSQOSPROFILE* lpGprsQosProfile)
{
    FUNCTION_TRACE(RILDrv_SetRequestedQualityOfService);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_SetMinimumQualityOfService\r\n")));
    return SetQOS(dwParam,lpGprsQosProfile, "+CGQMIN",APIID_SETMINIMUMQUALITYOFSERVICE);
}

HRESULT DeleteQOS(DWORD dwParam, DWORD dwContextID, LPCSTR szATCmd, const APIID apiid)
{
    FUNCTION_TRACE(DeleteQOS);

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_PATH];
    (void)_snprintfz(szCmd, MAX_PATH, "AT%s=%u\r", szATCmd, dwContextID);

    if (!QueueCmd(pHandle,
                  szCmd,
#ifdef GPRS_QOS_DELETE_NOOP_HACK
    // QOS deletion happens before every GPRS call currently,
    // and on some radios this is not a good idea.  For now,
    // just NOOP the QOS deletion
                  CMDOPT_NOOP,
#else
                  CMDOPT_NONE,
#endif
                  apiid,
                  NULL,     // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGQREQ=<dwContextID>
HRESULT RILDrv_DeleteRequestedQualityOfService (DWORD dwParam, DWORD dwContextID)
{
    FUNCTION_TRACE(RILDrv_DeleteRequestedQualityOfService);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_DeleteRequestedQualityOfService\r\n")));

    return DeleteQOS(dwParam, dwContextID, "+CGQREQ", APIID_DELETEREQUESTEDQUALITYOFSERVICE);
}

// +CGQMIN=<dwContextID)
HRESULT RILDrv_DeleteMinimumQualityOfService (DWORD dwParam, DWORD dwContextID)
{
    FUNCTION_TRACE(RILDrv_DeleteMinimumQualityOfService);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_DeleteMinimumQualityOfService\r\n")));

    return DeleteQOS(dwParam, dwContextID, "+CGQMIN", APIID_DELETEMINIMUMQUALITYOFSERVICE);
}

// +CGATT=[0|1]
HRESULT RILDrv_SetGPRSAttached (DWORD dwParam, BOOL fAttached)
{
    FUNCTION_TRACE(RILDrv_SetGPRSAttached);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_SetGPRSAttached\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_PATH];
    (void)_snprintfz(szCmd, MAX_PATH, "AT+CGATT=%u\r", fAttached ? 1 : 0);

    if (!QueueCmd(pHandle,
                  szCmd,
                  CMDOPT_NONE,
                  APIID_SETGPRSATTACHED,
                  NULL,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

/*
+CGATT: <state>
<state>: indicates the state of GPRS attachment
0 - detached
1 - attached
*/
HRESULT ParseGetGPRSAttached(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGPRSAttached);

    UINT nValue;
    HRESULT hr = E_FAIL;
    BOOL *pbResult=NULL;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+CGATT: "
    if (!MatchStringBeginning(szRsp, "+CGATT: ", szRsp))
    {
        goto Error;
    }

    // Parse <state>
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        goto Error;
    }

    pbResult = (BOOL*)AllocBlob(sizeof(BOOL));
    if (!pbResult)
    {
        goto Error;
    }
    *pbResult=nValue;
    pBlob = (void*)pbResult;
    cbBlob = sizeof(BOOL);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pbResult);
    }

    return hr;
}

// +CGATT?
HRESULT RILDrv_GetGPRSAttached (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetGPRSAttached);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_GetGPRSAttached\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

#ifdef RIL_RADIO_RESILIENCE
    if (ShouldSpoofCommand())
    {
        // If the radio was reset recently, just return FALSE.  Otherwise this may get
        // queued up after all the init commands, and take a long time to execute.
        // This is bad, since we want to handle the RIL_NOTIFY_DISCONNECT that
        // is sent up during reset so the UI can be updated quickly
        BOOL* pfAttachState = (BOOL*)AllocBlob(sizeof(BOOL));
        if (!pfAttachState) {
            hr = E_OUTOFMEMORY;
            goto Error;
        }
        hr = pHandle->GetDevice()->GetNextCmdID();
        *pfAttachState = FALSE;
        pHandle->Notify(RIL_RESULT_OK, hr, pfAttachState, sizeof(BOOL));
    }
    else
#endif // RIL_RADIO_RESILIENCE
    {
        if (!QueueCmd(pHandle,
                      "AT+CGATT?\r",
                      CMDOPT_NONE,
                      APIID_GETGPRSATTACHED,
                      ParseGetGPRSAttached,  // Parse fn
                      NULL,     // Ptr to notification data
                      hr))
        {
            hr=E_FAIL;
            goto Error;
        }
    }
    Error:
    return hr;
}

// +CGACT=[0|1],<dwContextID>[,<dwContextID>...]
HRESULT RILDrv_SetGPRSContextActivated (DWORD dwParam, DWORD dwContextID, BOOL fContextActivation)
{
    FUNCTION_TRACE(RILDrv_SetGPRSContextActivated);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_SetGPRSContextActivated\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_PATH];
    (void)_snprintfz(szCmd, MAX_PATH, "AT+CGACT=%u,%u\r", fContextActivation ? 1 : 0, dwContextID);

    // CMDOPT_NOOP is used when fContextActivation=1 because:
    //   If GPRS context activation requires a username/password, then that information would need to be
    //   provided at the time of activation - but the activating entity (CellTSP) does not have access
    //   to the username/password - therefore, we're going to defer activation to the time of
    //   RILDrv_EnterGPRSDataMode and allow the PPP negotiation process to provide the username/password
    //   as it does with normal CSD RAS connections
   
    if (!QueueCmd(pHandle,
        szCmd,
        fContextActivation ? CMDOPT_NOOP : CMDOPT_DEACT,
        APIID_SETGPRSCONTEXTACTIVATED,
        NULL,  // Parse fn
        NULL,  // Ptr to notification data
        hr)) {
        hr=E_FAIL;
        goto Error;
        }

    Error:
    return hr;
}

/*
+CGACT: <cid>, <state>
[<CR><LF>+CGACT: <cid>, <state>
[...]]
*/
HRESULT ParseGetGPRSContextActivatedList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGPRSContextActivatedList);
    PBYTE pBuffer=NULL;
    DWORD dwBufferSize=0;

    RILGPRSCONTEXTACTIVATED *pCtxtAct;

    UINT nValue;

    HRESULT hr = E_FAIL;
    BOOL bSuccess;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+CGACT: "
    while (MatchStringBeginning(szRsp, "+CGACT: ", szRsp))
    {
        // Reallocate our buffer for the additional data
        pBuffer = (PBYTE)ReallocBlob(pBuffer,dwBufferSize+sizeof(RILGPRSCONTEXTACTIVATED));
        if (!pBuffer)
        {
            goto Error;
        }

        pCtxtAct = (RILGPRSCONTEXTACTIVATED *)(pBuffer+dwBufferSize);
        memset(pCtxtAct, 0x00, sizeof(RILGPRSCONTEXTACTIVATED));
        pCtxtAct->cbSize=sizeof(RILGPRSCONTEXTACTIVATED);

        dwBufferSize+=sizeof(RILGPRSCONTEXTACTIVATED);

        // Parse <cid>
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
        {
            goto Continue;
        }
        pCtxtAct->dwContextID=nValue;

        // Parse ,<fActivated>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            (!ParseUInt(szRsp, TRUE, nValue, szRsp)) )
        {
            goto Continue;
        }
        pCtxtAct->fActivated = nValue;

        Continue:
        // Find "<postfix>"
        bSuccess = FindRspPostfix(szRsp, szRsp);
        if (!bSuccess)
        {
            goto Error;
        }
    }

    pBlob = (void*)pBuffer;
    cbBlob = dwBufferSize;
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pBuffer);
    }
    return hr;
}

// +CGACT?
HRESULT RILDrv_GetGPRSContextActivatedList (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetGPRSContextActivatedList);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_GetGPRSContextActivatedList\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!QueueCmd(pHandle,
                  "AT+CGACT?\r",
                  CMDOPT_NONE,
                  APIID_GETGPRSCONTEXTACTIVATEDLIST,
                  ParseGetGPRSContextActivatedList,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

HRESULT ParseForceFailureResponse(LPCSTR, void*&, UINT&)
{
    // Force a failure
    return E_FAIL;
}

// +CGDATA=<params>
HRESULT RILDrv_EnterGPRSDataMode (DWORD dwParam, const RILENTERGPRSDATAMODE * lpEnterGprsDataMode)
{
    FUNCTION_TRACE(RILDrv_EnterGPRSDataMode);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_EnterGPRSDataMode\r\n")));

    HRESULT hr = E_INVALIDARG;
    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    DWORD dwOptions = CMDOPT_NONE;
    PFN_CMD_PARSE pfnParse = NULL;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpEnterGprsDataMode)
    {
        goto Error;
    }

#if defined (GPRSDIAL_REQUIRES_REGISTRATION) || defined (GPRSDIAL_REQUIRES_GPRSREGISTRATION)
    if(((g_dwRegStatus != RIL_REGSTAT_HOME) && (g_dwRegStatus != RIL_REGSTAT_ROAMING))
#if defined (GPRSDIAL_REQUIRES_GPRSREGISTRATION)
        || ((g_dwGPRSRegStatus != RIL_REGSTAT_HOME) && (g_dwGPRSRegStatus != RIL_REGSTAT_ROAMING))
#endif // #if defined (GPRSDIAL_REQUIRES_GPRSREGISTRATION)
        )
    {
        dwOptions = CMDOPT_NOOP;
         pfnParse = ParseForceFailureResponse;
    }
#endif //defined (GPRSDIAL_REQUIRES_REGISTRATION) || defined (GPRSDIAL_REQUIRES_GPRSREGISTRATION)

#if defined(OEM1_DRIVER) || defined(OEM2_DRIVER) || defined(WAVECOM_DRIVER)
    // Send "ATD*99#" instead of "AT+CDATA" because the former is tested by OEM1 and acts in the same manner as far as we're concerned
    // Parameters to RILDrv_EnterGPRSDataMode() may be ignored or arguments can be supplied as part of the dial string instead
    // strcpy(szCmd,"ATD*99#\r");

     // Use a more flexible dial string instead (D*<GPRS_SC>[*[<called_address>][*[<L2P>][*[<cid>]]]]#).
    strcpy(szCmd,"ATD*99***");
    szWalk = strchr(szWalk, '\0');
    DEBUGCHK(NULL != szWalk);

    // In theory we can specify more than one PDP contexts to activate, but since we only support
    // one right now, simply choose the first one in the array.
    if (lpEnterGprsDataMode->dwNumContexts >= 1)
    {
        (void)_snprintfz( szWalk, MAX_PATH - (szWalk - szCmd), "%u", lpEnterGprsDataMode->dwContextID[0] );
        szWalk = strchr(szWalk, '\0');
        DEBUGCHK(NULL != szWalk);
    }

    // Add "#\r"
    (void)strncpyz(szWalk, "#\r", MAX_PATH - (szWalk -szCmd));

#else
    strcpy(szCmd,"AT+CGDATA=");
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    if (lpEnterGprsDataMode)
    {

        // Build command
        LPSTR szString;
        if (!StringFromFlag(lpEnterGprsDataMode->dwL2Protocol,g_GprsL2Protocols, NUM_GPRSL2PROTOCOLS,szString))
        {
            hr = E_INVALIDARG;
            goto Error;
        }
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), "\"%s\"",szString);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);

        for (DWORD i=0;i<lpEnterGprsDataMode->dwNumContexts;i++)
        {
            (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), ",%u", lpEnterGprsDataMode->dwContextID[i]);
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            DEBUGCHK(NULL != szWalk);
        }
    }

    // Add "\r"
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));
#endif

#ifdef EMP_DRIVER
    // Look up the mapping table to get a VSP for sending AT+CGDATA command
    HANDLE hVeiHandle = INVALID_HANDLE_VALUE;
    CCidSerialPortHandleMap* pCidHandleMap = CCidSerialPortHandleMap::Instance();
    pCidHandleMap->GetSerialPortHandleFromContextID(lpEnterGprsDataMode->dwContextID[0], &hVeiHandle);
    if (INVALID_HANDLE_VALUE == hVeiHandle)
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : Error : RILDrv_EnterGPRSDataMode : Failed to get VEI handle for context %d\r\n"), lpEnterGprsDataMode->dwContextID[0]));
        hr = E_FAIL;
        goto Error;
    }

    // Send an empty AT command so that the response and notifications after AT+CGDATA can still be processed
    if (!QueueCmd(pHandle,
                "AT\r",
                CMDOPT_NOOP,
                APIID_ENTERGPRSDATAMODE,
                pfnParse,
                NULL,
                hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Send an AT command through CAIF driver
    const DWORD IOCTL_CAIF_SEND_AT_COMMAND = CTL_CODE(FILE_DEVICE_UNKNOWN, 0x901, METHOD_BUFFERED, FILE_ANY_ACCESS);
    if (!DeviceIoControl(hVeiHandle, IOCTL_CAIF_SEND_AT_COMMAND, szCmd, strlen(szCmd), NULL, 0, NULL, NULL))
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : Error : RILDrv_EnterGPRSDataMode : Failed to send %s\r\n"), TString(PrintableString(szCmd, strlen(szCmd)))));
        hr = E_FAIL;
        goto Error;
    }
    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : Sending cmd: %s\r\n"), TString(PrintableString(szCmd, strlen(szCmd)))));
#else
    if (!QueueCmd(pHandle,
                  szCmd,
                  dwOptions,
                  APIID_ENTERGPRSDATAMODE,
                  pfnParse,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }
#endif

    Error:
    return hr;
}
/*
+CGPADDR: <cid>,<PDP_addr>
[<CR><LF>+CGPADDR: <cid>,<PDP_addr>
[...]]
*/
HRESULT ParseGetGPRSAddress(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGPRSAddress);

    UINT nValue;
    HRESULT hr = E_FAIL;
    LPCSTR szAddr, szAddrEnd;
    LPTSTR wszAddrReturned=NULL;
    DWORD dwStringLength=0;
    pBlob = NULL;
    cbBlob = 0;

    // Note: The pdp address is optional, so ignore it if it's not there and return an empty string
    // Also, ericsson doesn't even give us the cid, so ignore that if it's not present also

    if (  (!ParseRspPrefix(szRsp, szRsp))
       || (!MatchStringBeginning(szRsp, "+CGPADDR: ", szRsp))
       || (!ParseUInt(szRsp, TRUE, nValue, szRsp))
       || (!MatchStringBeginning(szRsp, ",", szRsp)) )
    {
        // Rig szAddr so that if we don't find anything, we'll return an empty string
        szAddr=szAddrEnd="\r\n";
    }
    else
    {
        szAddr = szRsp;

        // Parse ,<pd1>[,...[,pdN]]
        szAddrEnd = strstr(szAddr, "\r\n");
        if (!szAddrEnd)
        {
            goto Error;
        }

        szRsp = szAddrEnd;
    }

    // Set dwParameterLength to be size of string in bytes counting null char at end
    dwStringLength = ((szAddrEnd-szAddr)+1);
    wszAddrReturned = (LPTSTR)AllocBlob(dwStringLength * sizeof(TCHAR));
    if (!wszAddrReturned)
    {
        goto Error;
    }

    (void)wcsncpyz(wszAddrReturned, WideString(szAddr,'\r'), dwStringLength);

    pBlob = (void*)wszAddrReturned;
    cbBlob = dwStringLength * sizeof(TCHAR);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(wszAddrReturned);
    }

    return hr;
}

// +CGPADDR=<dwContextID>[,<dwContextID>...]
HRESULT RILDrv_GetGPRSAddress (DWORD dwParam, DWORD dwContextID)
{
    FUNCTION_TRACE(RILDrv_GetGPRSAddress);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_GetGPRSAddress\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_PATH];
    (void)_snprintfz(szCmd, MAX_PATH, "AT+CGPADDR=%u\r", dwContextID);

    if (!QueueCmd(pHandle,
                  szCmd,
                  CMDOPT_NONE,
                  APIID_GETGPRSADDRESS,
                  ParseGetGPRSAddress,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGANS=<params>
/*
typedef struct rilgprsanswer_tag
{
    DWORD cbSize;                       // @field structure size in bytes
    BOOL fAnswer;                       // @parm TRUE: accept, FALSE: reject
    DWORD dwL2Protocol;                 // @parm an optional RILL2PROTOCOL_* constant
    DWORD dwNumContexts;                // @parm number of contexts which follow
    DWORD dwContextID[];                // @parm identifies the context(s) to enter data state
} RILGPRSANSWER, *LPRILGPRSANSWER;
*/
HRESULT RILDrv_GPRSAnswer (DWORD dwParam, const RILGPRSANSWER *lpGprsAnswer)
{
    FUNCTION_TRACE(RILDrv_GPRSAnswer);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_GPRSAnswer\r\n")));

    HRESULT hr = E_INVALIDARG;
    char szCmd[MAX_PATH];
    LPSTR szWalk = szCmd;
    DWORD i;

#ifdef EMP_DRIVER
    // EMP does not support +CGANS
    hr = E_NOTIMPL;
    goto Error;
#endif

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle || !lpGprsAnswer)
    {
        goto Error;
    }

    // Build command

    (void)_snprintfz(szCmd, MAX_PATH, "AT+CGANS=%u,", lpGprsAnswer->fAnswer ? 1 : 0);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    LPSTR szString;
    if (!StringFromFlag(lpGprsAnswer->dwL2Protocol,g_GprsL2Protocols, NUM_GPRSL2PROTOCOLS,szString))
    {
        goto Error;
    }
    (void)_snprintfz(szWalk, MAX_PATH, "\"%s\"", szString);
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    DEBUGCHK(NULL != szWalk);

    for (i=0;i<lpGprsAnswer->dwNumContexts;i++)
    {
        (void)_snprintfz(szWalk, MAX_PATH - (szWalk - szCmd), ",%u", lpGprsAnswer->dwContextID[i]);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        DEBUGCHK(NULL != szWalk);
    }

    // Add "\r"
    (void)strncpyz(szWalk, "\r", MAX_PATH - (szWalk - szCmd));

    if (!QueueCmd(pHandle,
                  szCmd,
                  CMDOPT_NONE,
                  APIID_GPRSANSWER,
                  NULL,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

/*
+CGREG: <n>,<stat>[,<lac>,<ci>]
*/
HRESULT ParseGetGPRSRegistrationStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGPRSRegistrationStatus);
    UINT nValue;
    HRESULT hr = E_FAIL;
    DWORD *pdwRegStatus=NULL;
    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+CGREG: "
    if (!MatchStringBeginning(szRsp, "+CGREG:", szRsp))
    {
        goto Error;
    }

    // Parse <n> and throw away
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        goto Error;
    }

    // Parse ","
    if (!MatchStringBeginning(szRsp, ",", szRsp))
    {
        goto Error;
    }

    // Parse <stat>
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        goto Error;
    }

    pdwRegStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwRegStatus)
    {
        goto Error;
    }

    if (NUM_REGSTATS > nValue)
    {
        *pdwRegStatus = g_rgdwRegStats[nValue];
    }
    else
    {
        *pdwRegStatus = RIL_REGSTAT_UNKNOWN;
    }
    g_dwGPRSRegStatus = *pdwRegStatus;
    if( RIL_REGSTAT_HOME == g_dwGPRSRegStatus || RIL_REGSTAT_ROAMING == g_dwGPRSRegStatus )
    {
        RILDrv_GetAccessTech();
    }
#ifdef RIL_WATSON_REPORT
    // Copy gprs registration status to the info cache.
    g_RilInfoCache.dwGPRSRegStatus = *pdwRegStatus;
#endif // RIL_WATSON_REPORT

    pBlob = (void*)pdwRegStatus;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwRegStatus);
    }

    return hr;
}

// +CGREG?
HRESULT RILDrv_GetGPRSRegistrationStatus (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetGPRSRegistrationStatus);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_GetGPRSRegistrationStatus\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }


    if (!QueueCmd(pHandle,
                  "AT+CGREG?\r",
                  CMDOPT_NONE,
                  APIID_GETGPRSREGISTRATIONSTATUS,
                  ParseGetGPRSRegistrationStatus,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

/*
+CGCLASS: <class>
<class>: a string parameter which indicates the GPRS mobile class (in descending order of functionality)
A class A (highest)
B classB
C class C in GPRS and circuit switched alternate mode
CG class C in GPRS only mode
CC class C in circuit switched only mode (lowest)
*/
HRESULT ParseGetGPRSClass(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGPRSClass);
    char szValue[MAX_PATH];
    DWORD *pdwClass=NULL;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+CGCLASS: "
    if (!MatchStringBeginning(szRsp, "+CGCLASS: ", szRsp))
    {
        goto Error;
    }

    pdwClass = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwClass)
    {
        goto Error;
    }

    // Parse <class>
    if (!ParseString(szRsp, szValue, MAX_PATH, szRsp))
    {
        goto Error;
    }

    FlagFromString(szValue,g_GprsClass,NUM_GPRSCLASS,*pdwClass);

    pBlob = (void*)pdwClass;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwClass);
    }

    return hr;
}

// +CGCLASS?
HRESULT RILDrv_GetGPRSClass (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetGPRSClass);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_GetGPRSClass\r\n")));

    HRESULT hr = E_INVALIDARG;

#ifdef EMP_DRIVER
    // EMP does not support +CGCLASS
    hr = E_NOTIMPL;
    goto Error;
#endif

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }


    if (!QueueCmd(pHandle,
                  "AT+CGCLASS?\r",
                  CMDOPT_NONE,
                  APIID_GETGPRSCLASS,
                  ParseGetGPRSClass,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGCLASS=[A|B|C|CG|CC]
HRESULT RILDrv_SetGPRSClass (DWORD dwParam, DWORD dwClass)
{
    FUNCTION_TRACE(RILDrv_SetGPRSClass);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_SetGPRSClass\r\n")));

    HRESULT hr = E_INVALIDARG;

#ifdef EMP_DRIVER
    // EMP does not support +CGCLASS
    hr = E_NOTIMPL;
    goto Error;
#endif

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_PATH];
    LPSTR szString;
    if (!StringFromFlag(dwClass,g_GprsClass,NUM_GPRSCLASS,szString))
    {
        goto Error;
    }

    (void)_snprintfz(szCmd, MAX_PATH, "AT+CGCLASS=\"%s\"\r", szString);

    if (!QueueCmd(pHandle,
                  szCmd,
                  CMDOPT_NONE,
                  APIID_SETGPRSCLASS,
                  NULL,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr=E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

/*
+CGSMS: <service>
<service>: a numeric parameter which indicates the service or service preference to be used
0 GPRS
1 circuit switched
2 GPRS preferred (use circuit switched if GPRS not available)
3 circuit switched preferred (use GPRS if circuit switched not available)
*/
HRESULT ParseGetMOSMSService(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetMOSMSService);
    UINT nValue;
    HRESULT hr = E_FAIL;
    DWORD *pdwMoSmsService=NULL;
    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+CGSMS: "
    if (!MatchStringBeginning(szRsp, "+CGSMS: ", szRsp))
    {
        goto Error;
    }

    // Parse <service>
    if (!ParseUInt(szRsp, TRUE, nValue, szRsp))
    {
        goto Error;
    }

    pdwMoSmsService = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwMoSmsService)
    {
        goto Error;
    }

    FlagFromValue(nValue,g_GprsMOSMSService,NUM_GPRSMOSMSSERVICE,*pdwMoSmsService);

    pBlob = (void*)pdwMoSmsService;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwMoSmsService);
    }

    return hr;
}

// +CGSMS?
HRESULT RILDrv_GetMOSMSService (DWORD dwParam)
{
    FUNCTION_TRACE(RILDrv_GetMOSMSService);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_GetMOSMSService\r\n")));

    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    if (!QueueCmd(pHandle,
                  "AT+CGSMS?\r",
                  CMDOPT_NONE,
                  APIID_GETMOSMSSERVICE,
                  ParseGetMOSMSService,  // Parse fn
                  NULL,     // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}

// +CGSMS=[0|1|2|3]
HRESULT RILDrv_SetMOSMSService (DWORD dwParam, DWORD dwMoSmsService)
{
    FUNCTION_TRACE(RILDrv_SetMOSMSService);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : E : RILDrv_SetMOSMSService\r\n")));

    UINT Value;
    HRESULT hr = E_INVALIDARG;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle)
    {
        goto Error;
    }

    // Build command
    char szCmd[MAX_PATH];
    if (!ValueFromFlag(dwMoSmsService,g_GprsMOSMSService,NUM_GPRSMOSMSSERVICE,Value))
    {
        goto Error;
    }
    (void)_snprintfz(szCmd, MAX_PATH, "AT+CGSMS=%u\r",Value);


    if (!QueueCmd(pHandle,
                  szCmd,
                  CMDOPT_NONE,
                  APIID_SETMOSMSSERVICE,
                  NULL,  // Parse fn
                  NULL,  // Ptr to notification data
                  hr))
    {
        hr = E_FAIL;
        goto Error;
    }

    Error:
    return hr;
}


// +CGCLASS: (list of supported <class>s)
HRESULT ParseGetGprsCapsClass(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGprsCapsClass);
    DWORD *pdwClass = NULL;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+CGCLASS: "
    if (!MatchStringBeginning(szRsp, "+CGCLASS: ", szRsp))
    {
        goto Error;
    }

    pdwClass = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwClass)
    {
        goto Error;
    }

    // Parse ,<class>
    if (!ParseStringList(szRsp, g_GprsClass, NUM_GPRSCLASS, *pdwClass, szRsp))
    {
        goto Error;
    }

    pBlob = (void*)pdwClass;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwClass);
    }

    return hr;
}

// +CGDCONT: (range of supported <cid>s),<PDP_type>,,,(list of supported <d_comp>s),(list of supported <h_comp>s)[,(list of supported <pd1>s)[,...[,(list of supported <pdN>s)]]]
// [<CR><LF>+CGDCONT: (range of supported <cid>s),<PDP_type>,,,(list of supported <d_comp>s),(list of supported <h_comp>s)[,(list of supported <pd1>s)[,...[,(list of supported <pdN>s)]]]
// [...]]
HRESULT ParseGetGprsCapsContext(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGprsCapsContext);

    PBYTE pBuffer=NULL;
    DWORD dwBufferSize=0;

    RILGPRSCONTEXTCAPS GPRSContextCaps, *pGPRSContextCaps;

    char szValue[MAX_PATH];
    LPCSTR szParams, szParamsEnd;

    HRESULT hr = E_FAIL;
    BOOL bSuccess;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+CGDCONT: "
    while (MatchStringBeginning(szRsp, "+CGDCONT: ", szRsp))
    {
        memset(&GPRSContextCaps, 0x00, sizeof(GPRSContextCaps));

        // Parse (range of supported <cid>s)
        if (!ParseRange(szRsp, GPRSContextCaps.ContextIDRange, szRsp))
        {
            goto Continue;
        }
        GPRSContextCaps.dwParams|= RIL_PARAM_GCONT_CONTEXTID;

        // Parse ,<PDP_type>
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseString(szRsp, szValue, MAX_PATH, szRsp))
        {
            goto Continue;
        }

        if (FlagFromString(szValue, g_GprsPDPTypes, NUM_GPRSPDPTYPES, GPRSContextCaps.dwProtocolType))
        {
            GPRSContextCaps.dwParams|= RIL_PARAM_GCONT_PROTOCOLTYPE;
        }

        // Parse ,,,(list of supported <d_comp>s)
        if (!MatchStringBeginning(szRsp, ",,,", szRsp) ||
            !ParseRangeList(szRsp, g_GprsDataComp, NUM_GPRSDATACOMP, GPRSContextCaps.dwDataCompression, szRsp))
        {
            goto Continue;
        }
        GPRSContextCaps.dwParams|= RIL_PARAM_GCONT_DATACOMPRESSION;

        // Parse ,(list of supported <h_comp>s)
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
            !ParseRangeList(szRsp, g_GprsHeaderComp, NUM_GPRSHEADERCOMP, GPRSContextCaps.dwHeaderCompression, szRsp))
        {
            goto Continue;
        }
        GPRSContextCaps.dwParams|= RIL_PARAM_GCONT_HEADERCOMPRESSION;

        // Parse ","
        if (!MatchStringBeginning(szRsp, ",", szRsp))
        {
            goto Continue;
        }

        szParams = szRsp;
        // Parse ,<pd1>[,...[,pdN]]
        szParamsEnd = strstr(szParams, "\r\n");
        if (!szParamsEnd)
        {
            goto Error;
        }

        szRsp = szParamsEnd;

        // Set dwParameterLength to be size of string in bytes counting extra null char at end
        GPRSContextCaps.dwParameterLength = ((szParamsEnd-szParams)+1);

        // Now all the data between szParams and szParamsEnd is the pd1-N string
        GPRSContextCaps.dwParams|= RIL_PARAM_GCONT_PARAMETERS;

        Continue:
        // Find "<postfix>"
        bSuccess = FindRspPostfix(szRsp, szRsp);
        if (!bSuccess)
        {
            goto Error;
        }

        // Init size of GPRSContextCaps
        GPRSContextCaps.cbSize = sizeof(GPRSContextCaps);

        // Add in size of param string
        GPRSContextCaps.cbSize += GPRSContextCaps.dwParameterLength;

        // Round up to the nearest 32-bit address for alignment
        GPRSContextCaps.cbSize = (GPRSContextCaps.cbSize + 3) & (~0x03);

        // Reallocate our buffer for the additional data
        pBuffer = (PBYTE)ReallocBlob(pBuffer,dwBufferSize+GPRSContextCaps.cbSize);
        if (!pBuffer)
        {
            goto Error;
        }

        // Point pGPRSContextCaps to the first byte of the newly allocated section
        pGPRSContextCaps = (RILGPRSCONTEXTCAPS *) (pBuffer+dwBufferSize);

        // Copy struct to buffer
        *pGPRSContextCaps = GPRSContextCaps;

        if (GPRSContextCaps.dwParameterLength)
        {
            DWORD dwLen;
            char *szDestParams = GPRSContextCaps.szParameters;

            // Build the individual param strings
            while (1)
            {
                // Find beginnning of list '('
                if ('(' != *szParams)
                {
                    goto Error;
                }
                szParams++;

                // Find end of list ')'
                szParamsEnd = strchr(szParams,')');
                if (!szParamsEnd)
                {
                    goto Error;
                }

                // Get length of string
                dwLen = szParamsEnd-szParams;

                // Copy string
                memcpy(szDestParams,szParams,dwLen);

                // Remember to terminate
                szDestParams[dwLen]='\0';

                // Update dest ptr
                szDestParams+=dwLen+1;

                // Update src ptr
                szParams=szParamsEnd+1;

                // Parse ','
                if (*szParams!=',')
                {
                    // Append final null char
                    *szDestParams=TEXT('\0');
                    break;
                }
            }
        }

        dwBufferSize+=GPRSContextCaps.cbSize;
    }

    pBlob = (void*)pBuffer;
    cbBlob = dwBufferSize;
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pBuffer);
    }
    return hr;
}

// +XXXXXX: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [<CR><LF>+XXXXXX: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [...]]
HRESULT ParseQOSCapsList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPCSTR szCmd)
{
    FUNCTION_TRACE(ParseGetRequestedQualityOfServiceList);
    PBYTE pBuffer=NULL;
    DWORD dwBufferSize=0;

    RILGPRSQOSPROFILECAPS *pQOSProfileCaps;

    char szValue[MAX_PATH];

    HRESULT hr = E_FAIL;
    BOOL bSuccess;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "+XXXXXX: "
    while (MatchStringBeginning(szRsp, szCmd, szRsp))
    {
        // Reallocate our buffer for the additional data
        pBuffer = (PBYTE)ReallocBlob(pBuffer,dwBufferSize+sizeof(RILGPRSQOSPROFILECAPS));
        if (!pBuffer)
        {
            goto Error;
        }

        pQOSProfileCaps = (RILGPRSQOSPROFILECAPS *)(pBuffer+dwBufferSize);
        memset(pQOSProfileCaps, 0x00, sizeof(RILGPRSQOSPROFILECAPS));
        pQOSProfileCaps->cbSize=sizeof(RILGPRSQOSPROFILECAPS);

        dwBufferSize+=sizeof(RILGPRSQOSPROFILECAPS);

        // Parse <PDP_type>
        if (!ParseString(szRsp, szValue, MAX_PATH, szRsp))
        {
            goto Continue;
        }

        FlagFromString(szValue, g_GprsPDPTypes, NUM_GPRSPDPTYPES,pQOSProfileCaps->dwProtocolType);

        // Parse ,(list of supported <precedence>s)
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
           (!ParseRangeList(szRsp, g_GprsPrecedenceClass, NUM_GPRSPRECEDENCECLASS, pQOSProfileCaps->dwPrecedenceClass, szRsp)) )
        {
           goto Continue;
        }
        pQOSProfileCaps->dwParams|= RIL_PARAM_GQOSP_PRECEDENCECLASS;

        // ,(list of supported <delay>s)
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
           (!ParseRangeList(szRsp, g_GprsDelayClass,NUM_GPRSDELAYCLASS, pQOSProfileCaps->dwDelayClass, szRsp)) )
        {
            goto Continue;
        }
        pQOSProfileCaps->dwParams|= RIL_PARAM_GQOSP_DELAYCLASS;

        // Parse ,(list of supported <reliability>s)
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
           (!ParseRangeList(szRsp, g_GprsReliabilityClass,NUM_GPRSRELIABILITYCLASS, pQOSProfileCaps->dwReliabilityClass, szRsp)) )
        {
            goto Continue;
        }
        pQOSProfileCaps->dwParams|= RIL_PARAM_GQOSP_RELIABILITYCLASS;

        // Parse ,(list of supported <peak>s)
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
           (!ParseRangeList(szRsp, g_GprsPeakThruClass,NUM_GPRSPEAKTHRUCLASS, pQOSProfileCaps->dwPeakThruClass, szRsp)) )
        {
            goto Continue;
        }

        // Parse ,(list of supported <mean>s)
        if (!MatchStringBeginning(szRsp, ",", szRsp) ||
           (!ParseRangeList(szRsp, g_GprsMeanThruClass,NUM_GPRSMEANTHRUCLASS, pQOSProfileCaps->dwMeanThruClass, szRsp)) )
        {
            goto Continue;
        }
        pQOSProfileCaps->dwParams|= RIL_PARAM_GQOSP_MEANTHRUCLASS;

        Continue:
        // Find "<postfix>"
        bSuccess = FindRspPostfix(szRsp, szRsp);
        if (!bSuccess)
        {
            goto Error;
        }
    }

    pBlob = (void*)pBuffer;
    cbBlob = dwBufferSize;
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pBuffer);
    }
    return hr;
}

// +CGQREQ: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [<CR><LF>+CGQREQ: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [...]]
HRESULT ParseGetGprsCapsQOS(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGprsCapsQOS);
    return ParseQOSCapsList(szRsp,pBlob,cbBlob,"+CGQREQ: ");
}

// +CGQMIN: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [<CR><LF>+CGQMIN: <PDP_type>, (list of supported <precedence>s), (list of supported <delay>s), (list of supported <reliability>s) , (list of supported <peak>s), (list of supported <mean>s)
// [...]]
HRESULT ParseGetGprsCapsQOSMin(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGprsCapsQOS);
    return ParseQOSCapsList(szRsp,pBlob,cbBlob,"+CGQMIN: ");
}

// +CGSMS: (list of currently available <service>s)
HRESULT ParseGetGprsCapsSMOSMS(LPCSTR szRsp, void*& pBlob, UINT& cbBlob)
{
    FUNCTION_TRACE(ParseGetGprsCapsSMOSMS);
    HRESULT hr = E_FAIL;
    DWORD *pdwMoSmsService=NULL;
    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp))
    {
        goto Error;
    }

    // Parse "+CGSMS: "
    if (!MatchStringBeginning(szRsp, "+CGSMS: ", szRsp))
    {
        goto Error;
    }

    pdwMoSmsService = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwMoSmsService)
    {
        goto Error;
    }

    // Parse (list of currently available <service>s)
    if (!ParseRangeList(szRsp, g_GprsMOSMSService,NUM_GPRSMOSMSSERVICE, *pdwMoSmsService, szRsp))
    {
        goto Error;
    }

    pBlob = (void*)pdwMoSmsService;
    cbBlob = sizeof(DWORD);
    hr = S_OK;

    Error:
    if (FAILED(hr))
    {
        FreeBlob(pdwMoSmsService);
    }

    return hr;
}

#ifdef EMP_DRIVER
// For EMP multiple PDP context support
CCidSerialPortHandleMap::CCidSerialPortHandleMap()
{
    BYTE i;
    // Initialize three VEI channels (VEI3, VEI4 and VEI5) to support multiple PDP contexts
    for (i=0; i<MaxChannelId; i++)
    {
        // Init member variables
        m_ContextID[i] = InvalidContextId;
        swprintf(m_SerialPortName[i], TEXT("VEI%d:"), i+3);
        m_SerialPortHandle[i] = INVALID_HANDLE_VALUE;
    }
}

CCidSerialPortHandleMap* CCidSerialPortHandleMap::Instance()
{
    static CCidSerialPortHandleMap CCidSerialPortHandleMapInstance;
    return &CCidSerialPortHandleMapInstance ;
}

HRESULT CCidSerialPortHandleMap::GetSerialPortNameFromContextID(DWORD dwContextID, TCHAR *tszDataPortName, DWORD dwSize, DWORD *pdwSizeOut)
{
    BYTE i;
    HRESULT hr = E_FAIL;

    if (NULL == tszDataPortName || NULL == pdwSizeOut)
    {
        RETAILMSG(MSG_ON, (TEXT("GetSerialPortNameFromContextID : Error parameters\r\n")));
        return hr;
    }

    *tszDataPortName = 0;
    *pdwSizeOut = 0;

    // Search for the specified context id
    for (i=0; i<MaxChannelId; i++)
    {
        if (dwContextID == m_ContextID[i])
        {
            break;
        }
    }

    // The specified context id is not in the list
    // Find an empty (and avaiable) entry for this context id
    if (i >= MaxChannelId)
    {
        for (i=0; i<MaxChannelId; i++)
        {
            if (InvalidContextId == m_ContextID[i])
            {
                m_ContextID[i] = dwContextID;
                break;
            }
        }
    }

    // Fail if the list is full
    if (i >= MaxChannelId)
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : Error : No VEI channel available for context %d!\r\n"), dwContextID));
        return hr;
    }

    // See if the buffer is large enough
    *pdwSizeOut = wcslen(m_SerialPortName[i]);
    if (dwSize < *pdwSizeOut)
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : Error : Buffer is too small for %s\r\n"), m_SerialPortName[i]));
        return hr;
    }

    _tcscpy(tszDataPortName, m_SerialPortName[i]);
    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : Got %s for context %d\r\n"), m_SerialPortName[i], m_ContextID[i]));
    hr = S_OK;
    return hr;
}

HRESULT CCidSerialPortHandleMap::GetSerialPortHandleFromContextID(DWORD dwContextID, HANDLE *lphSerial)
{
    BYTE i;
    HRESULT hr = E_FAIL;

    if (NULL == lphSerial)
    {
        RETAILMSG(MSG_ON, (TEXT("GetSerialPortHandleFromContextID : invalid pointer\r\n")));
        return hr;
    }
    *lphSerial = INVALID_HANDLE_VALUE;

    //  Search the context id list
    for (i=0; i<MaxChannelId; i++)
    {
        if (dwContextID == m_ContextID[i])
        {
            break;
        }
    }

    // The specified context id is not in the list
    // Find an empty (and avaiable) entry for this context id
    if (i >= MaxChannelId)
    {
        for (i=0; i<MaxChannelId; i++)
        {
            if (InvalidContextId == m_ContextID[i])
            {
                m_ContextID[i] = dwContextID;
                break;
            }
        }
    }

    // Fail if the list is full
    if (i >= MaxChannelId)
    {
        RETAILMSG(MSG_ON, (TEXT("RILDrv : Error : No VEI channel available for context %d!\r\n"), dwContextID));
        return hr;
    }

    if (INVALID_HANDLE_VALUE == m_SerialPortHandle[i])
    {
        m_SerialPortHandle[i] = CreateFile(m_SerialPortName[i], GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
        if (INVALID_HANDLE_VALUE == m_SerialPortHandle[i])
        {
            RETAILMSG(MSG_ON, (TEXT("RILDrv : Error : Failed to open %s for context %d\r\n"), m_SerialPortName[i], dwContextID));
            m_ContextID[i] = InvalidContextId;
            return hr;
        }
    }

    *lphSerial = m_SerialPortHandle[i];
    RETAILMSG(MSG_ON, (TEXT("RILDrv : i : Got %s handle 0x%X for context %d\r\n"), m_SerialPortName[i], m_SerialPortHandle[i], m_ContextID[i]));
    hr = S_OK;
    return hr;
}

#endif // EMP_DRIVER
